package br.com.felipeab.dexter.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;

import br.com.felipeab.dexter.expection.PersistenceException;

public class HibernateDAO<T, PK extends Serializable> extends HibernateFactory {

	private Transaction tx;
	private Session session;

	public HibernateDAO() {
		buildIfNeeded();
	}

	@SuppressWarnings("unchecked")
	public T save(T object) {
		try {
			startTransaction();
			object = (T) session.save(object);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return object;
	}

	public T saveOrUpdate(T object) {
		try {
			startTransaction();
			session.saveOrUpdate(object);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return object;
	}

	public T update(T object) {
		try {
			startTransaction();
			session.update(object);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return object;
	}

	public void delete(T object) {
		try {
			startTransaction();
			session.delete(object);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public T findById(Class clazz, Long id) {
		T object = null;
		try {
			startTransaction();
			object = (T) session.load(clazz, id);
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return object;
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T object, String[] excludeProperties, boolean enableLike) {
		List<T> list = null;
		try {
			startTransaction();
			Example example = Example.create(object).excludeZeroes();
			if (enableLike) {
				example.enableLike(MatchMode.START);
			}
			if (excludeProperties != null && excludeProperties.length > 0) {
				for (String exclude : excludeProperties) {
					example.excludeProperty(exclude);
				}
			}
			list = session.createCriteria(object.getClass()).add(example).list();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return list;
	}

	public List<T> findByExample(T object, String[] excludeProperties) {
		return findByExample(object, excludeProperties, true);
	}

	public List<T> findByExample(T object) {
		return findByExample(object, null, true);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findAll(Class clazz) {
		List<T> list = null;
		try {
			startTransaction();
			Query query = session.createQuery("from " + clazz.getName());
			list = query.list();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			close(session);
		}
		return list;
	}

	protected void handleException(HibernateException e) throws PersistenceException {
		HibernateFactory.rollback(tx);
		throw new PersistenceException(e);
	}

	public Session getSession() {
		return session;
	}

	private void startTransaction() {
		session = openSession();
		tx = session.beginTransaction();
	}
}
